package com.zoo.buyor.websocket.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.zoo.buyor.common.event.UserOnlineEvent;
import com.zoo.buyor.user.dao.UserDao;
import com.zoo.buyor.websocket.domain.enums.WSChannelExtraDTO;

import com.zoo.buyor.user.domain.entity.User;
import com.zoo.buyor.websocket.domain.vo.response.WSBaseResp;
import com.zoo.buyor.user.service.LoginService;
import com.zoo.buyor.websocket.service.WebSocketService;
import com.zoo.buyor.websocket.adapter.WebSocketAdapter;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
public class WebSocketServiceImpl implements WebSocketService {
    @Lazy
    @Autowired
    private WxMpService wxMpService;
    @Autowired
    private WebSocketAdapter webSocketAdapter;
    @Autowired
    LoginService loginService;
    @Autowired
    UserDao userDao;
    @Autowired
    ApplicationEventPublisher applicationEventPublisher;


    public static final Duration DURATION = Duration.ofHours(1);
    public static final int MAXIMUM_SIZE = 1000;
    private static final ConcurrentHashMap<Channel, WSChannelExtraDTO> ONLINE_WS_MAP = new ConcurrentHashMap<>();
    /**
     * 临时保存code和channel的映射关系
     */
    private static final Cache<Integer, Channel> WAIT_LOGIN_MAP = Caffeine.newBuilder()
            .maximumSize(MAXIMUM_SIZE)//最大waitLogin用户的数量
            .expireAfterWrite(DURATION)//过期时间
            .build();
    @Override
    public void connect(Channel channel) {
        ONLINE_WS_MAP.put(channel, new WSChannelExtraDTO());
    }

    @SneakyThrows
    @Override
    public void handleLoginReq(Channel channel) {
        // 处理认证请求逻辑
        //生成随机码,并把随机码与channel关联
        Integer eventKey = genLoginCodeAndReflectToChannel(channel);
        //找微信申请带参二维码
        WxMpQrCodeTicket wxMpQrCodeTicket = wxMpService.getQrcodeService().qrCodeCreateTmpTicket(eventKey, (int) DURATION.getSeconds());
        //把二维码推送给前端
        sendMsgToFrontEnd(channel,WebSocketAdapter.buildWsBaseResp(wxMpQrCodeTicket));

    }

    @Override
    public void offLine(Channel channel) {
        ONLINE_WS_MAP.remove(channel);
    }

    @Override
    public void login(Integer eventKey, User user) {
        Channel channel = WAIT_LOGIN_MAP.getIfPresent(eventKey);
        if (Objects.isNull(channel)){
            log.error("WAIT_LOGIN_MAP.code获取channel失败");
            return;
        }
        //移除eventKey
        WAIT_LOGIN_MAP.invalidate(eventKey);
        //获取登录token
        String token = loginService.loginToken(user.getId());
        ONLINE_WS_MAP.put(channel,new WSChannelExtraDTO(user.getId()));
        sendMsgToFrontEnd(channel,WebSocketAdapter.buildWSLoginResp(user,token));
        applicationEventPublisher.publishEvent(new UserOnlineEvent(this,user));
    }

    private void sendMsgToFrontEnd(Channel channel, WSBaseResp<?> wsBaseResp) {
        log.info(JSONUtil.toJsonStr(wsBaseResp));
        channel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(wsBaseResp)));
    }

    private Integer genLoginCodeAndReflectToChannel(Channel channel) {
        Integer eventKey;//用于维护与游客channel之间的映射
        do {
            eventKey = RandomUtil.randomInt(Integer.MAX_VALUE);
        } while (Objects.nonNull(WAIT_LOGIN_MAP.asMap().putIfAbsent(eventKey, channel)));
        return eventKey;
    }
}
